﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;


namespace XYRendering.Vegetation
{


    #region Prefab Data

    [Serializable]
    public class VGPrefabData : IEquatable<VGPrefabData>
    {
        public Mesh mesh;
        public Material material;
#if UNITY_EDITOR
        public string groupName = string.Empty;    // all instance of this prefab will group together

        public Vector3 groupLocalPosition = Vector3.zero;
        
        public Vector3 groupLocalScale = Vector3.one;
        
        public Vector3 groupLocalRotation = Vector3.zero;
        
        public int instancesCount = 0;
#endif
        public bool Equals(Mesh mesh, Material material)
        {
            return ReferenceEquals(this.mesh, mesh) && ReferenceEquals(this.material, material);
        }
        
        public bool Equals(VGPrefabData other)
        {
            return Equals(other.mesh, other.material);
        }
        
    }

    #endregion
    
    
    #region Instance Data

    [Serializable]
    public class VGInstanceData
    {
    #if UNITY_EDITOR
        public string name = string.Empty;
        public int layer = 0;
        public Vector3 localPosition = Vector3.zero;
        public Vector3 localScale = Vector3.one;
        public Vector3 localRotation = Vector3.zero;
        
    #endif
        public int prefabIndex = -1;
        public Matrix4x4 worldMatrix = Matrix4x4.identity;

        public void OnPreExecute(CommandBuffer cmd)
        {
            throw new NotImplementedException();
        }

        public void Execute(CommandBuffer cmd)
        {
            throw new NotImplementedException();
        }
    }
    
    #endregion
    

    // terrain/ground divided to N x N block at xz plane
    // every block contains multible instances
    #region VGBlockData

    [Serializable]
    public class VGBlockData
    {
        public Vector2Int blockIndex; // index of block, (0, 0) at left corner of xz plane
        
        public Bounds bounds; // bound of block in world space, will place a culling sphere at this bounding box at run time

        public List<VGInstanceData> instances; // key: index in lod group list of this object instance
                                                     // value: world matrix of this object instance
        public int instancesCount => instances.Count;

        [NonSerialized] public bool isVisible = true;
        
        [NonSerialized] public int lodLevel = 0;
        
        public VGBlockData(Vector2Int idx, Bounds bounds, int reserveInstancesCnt = 128)
        {
            this.blockIndex = idx;
            this.bounds = bounds;
            instances = new List<VGInstanceData>(reserveInstancesCnt);
        }
    }

    #endregion



    #region GPU Intancing Data

    [Serializable]
    public class VGInstancingData : ScriptableObject
    {
        public List<VGPrefabData> prefabs = new List<VGPrefabData>();
        
        public float blockHeightOffset; 
        
        public List<VGBlockData> blocks = new List<VGBlockData>();
        
        public int prefabsCount => prefabs.Count;
        
        public int blocksCount => blocks.Count;

        public int instancesCount
        {
            get
            {
                int result = 0;
                foreach (var block in blocks)
                {
                    result += block.instancesCount;
                }
                return result;
            }
        }


#if UNITY_EDITOR
        
        public VGPrefabData GetPrefab(Mesh mesh, Material material)
        {
            int idx = prefabs.FindIndex((p) => p.Equals(mesh, material));

            return idx < 0 ? null : prefabs[idx];
        }

        public VGPrefabData AddPrefab(Mesh mesh, Material material, Transform parent)
        {
            VGPrefabData prefab = new VGPrefabData();
            prefab.mesh = mesh;
            prefab.material = material;
            prefab.groupName = parent.gameObject.name;
            prefab.groupLocalPosition = parent.localPosition;
            prefab.groupLocalScale = parent.localScale;
            prefab.groupLocalRotation = parent.localEulerAngles;
            
            prefabs.Add(prefab);
            
            return prefab;
        }

        public VGBlockData GetBlock(Vector2Int idx)
        {
            int _idx = blocks.FindIndex(b => b.blockIndex == idx);
         
            return _idx < 0 ? null : blocks[_idx];
        }

        public VGBlockData AddBlock(Vector2Int idx, Bounds bounds)
        {
            VGBlockData block = new VGBlockData(idx, bounds);
            blocks.Add(block);
            
            return block;
        }
        

        public void ResetData()
        {
            blockHeightOffset = 0;
            prefabs.Clear();
            blocks.Clear();
        }
        
#endif        
    }

    #endregion
    
    
    
}